#include "ProxyServer.h"
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <ctype.h>
#include <sstream>

sockaddr_in ProxyServer::host_to_addr(std::string _host, unsigned short _port) {
	struct sockaddr_in output;
	struct hostent * he;

	output.sin_family = AF_INET;
	output.sin_port = htons(_port);

	// Get host by addr
	output.sin_addr.s_addr = inet_addr(_host.c_str());
	//output.sin_addr.s_addr = INADDR_ANY;

	return output;
}


void ProxyServer::send_client(int _sock, std::vector<char> data) {
	result = send(_sock, data.data(), data.size(),0);
	// Check for errors
	if(result < 0) {
		perror("send_client");
	}
}

void ProxyServer::stop_server() {
	close(incoming_sock);
}

ProxyConnection ProxyServer::receive_incoming_connection() {
	struct sockaddr_in newaddr;
	int len = sizeof(struct sockaddr_in);
	int newsock = accept(incoming_sock,
			(struct sockaddr*)&newaddr,
			(socklen_t*)&len);
	if(newsock < 0) {
		std::string err_msg = "";
		if(errno == ECONNABORTED) {
			err_msg = "Connection aborted.";
		}
		else if(errno == EPERM) {
			err_msg = "Incoming connection refused by firewall.";
		}
		else {
			err_msg = "Could not receive incoming connection.";
		}
		printf("Error: %s\n", err_msg.c_str());
	}
	/* CREATE ProxyConnection from new connection data */
	ProxyConnection newproxyconn;
	newproxyconn.sock = newsock;
	newproxyconn.addr = newaddr;
	return newproxyconn;
}

void ProxyServer::handle_server_connection(ProxyConnection conn){
	char* sock_message;
	int new_client_sockfd;
	maxfdp = incoming_sock;
	conn_number = 1;	
	fd_set fdsr;
	int _res;
	int fd_A[BACKLOG_MAX];
	for(int i=0; i<BACKLOG_MAX; i++){	
		fd_A[i] = 0;		
	}
	fd_A[0]=conn.sock;
	if(conn.sock>maxfdp){
		maxfdp = conn.sock;		
	}
	//loop
	while(1){
		_res = 0;
		FD_ZERO(&fdsr);
		FD_SET(incoming_sock, &fdsr);
		tv.tv_sec =  5;
		tv.tv_usec = 0;
		ProxyConnection newconn;
		int flag = 0;
		// push all connection to the fd set
		for(int i=0; i<BACKLOG_MAX; i++){
			if(fd_A[i]!=0){
				FD_SET(fd_A[i],&fdsr);
			}	
		}
		// execute select call
		_res = select(maxfdp+1, &fdsr, NULL, NULL, &tv);
		if(_res <0){
			perror("select");
			break;
		}else if(_res ==0){
			printf("timeout\n");
			continue;	
		}
		// check all sockfd in the fd set
		
		for(int j=0; j<BACKLOG_MAX; j++){
			if(FD_ISSET(fd_A[j],&fdsr)){
				//receive the GET message from client
				_res = recv(fd_A[j], sock_message, 1024,0);
				if(_res<=0){
					printf("client[%d] close\n",j);
					close(fd_A[j]);
					FD_CLR(fd_A[j],&fdsr);
					fd_A[j]=0;
					conn_number--;
					printf("server.conn_number:%d!!!!!\n",conn_number);
				}else{
					if(_res>0){
						//1.get url from the receive message
						string _url; 
						_url.assign(sock_message, _res);
						//2.find the cache according to the url
						int cache_no = search_cache(_url);
						if(cache_no == 0){
							//go through http server get new cache
							HTTPRequest *_http_req = (HTTPRequest*)new HTTPRequest(_url);
							//turn http_req to char and send to client;
							std::vector<char> _data = _http_req->to_array();
							//printf("%s^^^^^^^^^dddd^^^^^\n",_data.data());
							//printf("%d^^^^^^^^^ssss^^^^^\n",_data.size());
							send_client(fd_A[j], _data);
							//add the cache in map
							for(cache_it=cache.begin();
							    cache_it!=cache.end();cache_it++){
								if(cache_it->second != NULL){
									cache[cache_it->first] = _http_req;
									break;
								}
							}
						}else{
							if(cache_no!=0){
								cache_it = cache.find(cache_no);
								std::vector<char> _data; 
								_data = cache_it->second->to_array();
								send_client(fd_A[j], _data);
							}else{
								//expire;get new cache;
							}
						}
					}
				}
			}
		}
		//check client number
		if(FD_ISSET(incoming_sock, &fdsr)){
			newconn = receive_incoming_connection();
			new_client_sockfd = newconn.sock;
			printf("new_client_sockfd: [%d]\n", new_client_sockfd);
			if(new_client_sockfd <= 0){
				perror("new client");
				continue;
			}	
			//handle the new client
			if(conn_number<BACKLOG_MAX){
				for(int n=0;n<BACKLOG_MAX;n++){
					if(fd_A[n]==0){
						fd_A[n] = new_client_sockfd;
						break;				
					}				
				}
				conn_number++;
				printf("new client:%s\n", inet_ntoa(newconn.addr.sin_addr));
				if(new_client_sockfd>maxfdp){
					maxfdp = new_client_sockfd;		
				}	
				
			}else{
				printf("max connection arrive!!\n");
				close(new_client_sockfd);	
			}
		}	
	}	
}

int ProxyServer::search_cache(string _url){
	for(cache_it=cache.begin();cache_it!=cache.end();cache_it++){
		if(cache_it->second->url_identitiy == _url){
			return (cache_it->first);	
		}
	}
	return 0;
}



